package com.soup.hummingbird.module.spring;

import com.soup.hummingbird.module.AbstractCollect;
import com.soup.hummingbird.module.AgentLoader;
import com.soup.hummingbird.module.Collect;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;

/**
 * 〈spring service方法插桩〉<br>
 *
 * @author zhaoyi
 * @date 2021-04-06 17:14
 * @see [相关类/方法]（可选）
 * @since [产品/模块版本] （可选）
 */
public class SpringServiceCollect extends AbstractCollect implements Collect {

    public static SpringServiceCollect INSTANCE = new SpringServiceCollect();

    private static final String beginSrc;
    private static final String endSrc;
    private static final String errorSrc;

    static {
        // 插桩前置处理源码，使用了2个占位符，需要在transform方法中进行替换
        StringBuilder sbuilder = new StringBuilder();
        sbuilder.append("com.soup.hummingbird.module.spring.SpringServiceCollect instance = com.soup.hummingbird.module.spring.SpringServiceCollect.INSTANCE;\r\n");
        sbuilder.append("com.soup.hummingbird.module.AbstractCollect.Statistics statistic = instance.begin(\"%s\",\"%s\");");
        beginSrc = sbuilder.toString();

        // 插桩后置处理源码，调用end方法
        sbuilder = new StringBuilder();
        sbuilder.append("instance.end(statistic);");
        endSrc = sbuilder.toString();

        // 异常插桩处理源码，调用error方法
        sbuilder = new StringBuilder();
        sbuilder.append("instance.error(statistic,e);");
        errorSrc = sbuilder.toString();
    }

    @Override
    public void sendStatistics(Statistics stat) {
        sendStatisticByHttp(stat,"serviceLog");
    }

    @Override
    public boolean isCollectTarget(String className, ClassLoader loader, CtClass ctclass) {
        try {
            for (Object obj : ctclass.getAnnotations()) {
                if (obj.toString().startsWith("@org.springframework.stereotype.Service")) {
                    return true;
                }
            }
        } catch (ClassNotFoundException e) {
            System.err.println(String.format("bit apm run error targetClassName=%s errorMessage=%s",className,e.getClass().getSimpleName()+":"+e.getMessage()));
        }
        return false;
    }

    @Override
    public Statistics begin(String className, String method) {
        ServiceStatistics serviceStatistics = new ServiceStatistics(super.begin(className, method));
        serviceStatistics.serviceName = className;
        serviceStatistics.methodName = method;
        serviceStatistics.logType="service";
        return serviceStatistics;
    }

    @Override
    public byte[] transform(ClassLoader loader, String className, byte[] classfileBuffer, CtClass ctclass) throws Exception {
        AgentLoader byteLoader = new AgentLoader(className, loader, ctclass);
        for (CtMethod method : ctclass.getDeclaredMethods()) {
            // 屏蔽非公共方法
            if (!Modifier.isPublic(method.getModifiers())) {
                continue;
            }
            // 屏蔽静态方法
            if (Modifier.isStatic(method.getModifiers())) {
                continue;
            }
            // 屏蔽本地方法
            if (Modifier.isNative(method.getModifiers())) {
                continue;
            }

            AgentLoader.MethodSrcBuild build = new AgentLoader.MethodSrcBuild();
            build.setBeginSrc(String.format(beginSrc, className, method.getName()));
            build.setEndSrc(endSrc);
            build.setErrorSrc(errorSrc);
            byteLoader.updateMethod(method, build);
        }
        return byteLoader.toBytecode();
    }

    public static class ServiceStatistics extends Statistics {
        public String serviceName; //服务名称
        public String methodName;// 方法名称
        public ServiceStatistics(Statistics s) {
            super(s);
        }
    }
}
